package edu.math.chaos.itfunc.alg;

import edu.math.chaos.itfunc.api.Parameters;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * User: Илья
 * Date: 09.11.13
 * Time: 12:31
 */
public class FractalBuilder {

	private Parameters params;
	private double[][] coeffs;
	private RandomQuantity randomQuantity;

	/**
	 * Строит фрактал с помощью CИФ по рандомизированному алгоритму.
	 * @return Список точек фрактального множества. Элементы списка - int[2],
	 * соответствуют точкам (x, y).
	 */
	public List<int[]> build(Parameters params, double[][] coeffs) {
		if (params == null) {
			throw new RuntimeException("Не заданы параметры");
		}

		Parameters.ErrorInfo error = params.checkParameters();
		if (error != null) {
			throw new RuntimeException(error.getMessage());
		}

		if (coeffs == null || coeffs.length == 0) {
			throw new RuntimeException("Не задана матрица коэффициентов");
		}

		this.params = params;
		this.coeffs = coeffs;
		this.randomQuantity = new RandomQuantity(makeProbabilityVector());

		return buildDots();
	}

	private double[] makeProbabilityVector() {
		double[] p = new double[coeffs.length];
		for (int i = 0; i < p.length; i++) {
			p[i] = coeffs[i][coeffs[i].length - 1];
		}

		return p;
	}

	private List<int[]> buildDots() {
		List<int[]> dots = new ArrayList<>();
		int[] prev = { params.getInitX(), params.getInitY() };
		dots.add(prev);

		int count = params.getIterations();
		for (int i = 0; i < count; i++) {
			int k = randomQuantity.next();

			double x = coeffs[k][0] * prev[0] + coeffs[k][1] * prev[1] + coeffs[k][4];
			double y = coeffs[k][2] * prev[0] + coeffs[k][3] * prev[1] + coeffs[k][5];

			int[] newDot = { (int) Math.round(x), (int) Math.round(y) };
			if (newDot[0] < 0 || newDot[0] >= params.getWidth() || newDot[1] < 0 || newDot[1] >= params.getHeight()) {
				System.out.println(String.format(
					"Итерация %d: точка (%d, %d) находится за границей области", i, newDot[0], newDot[1]));
				continue;
			}

			dots.add(newDot);
			prev = newDot;
		}

		return dots;
	}
}
